package ws.very.util.lang

import scala.reflect.ClassTag
import ShortTypes._

object FnCookies extends FnCookies

trait FnCookies {

  implicit class TimesHelper(number: Int) {
    def times[T](codeBlock: => T) =
      1 to number map { _ =>
        codeBlock
      }

    def *[T](codeBlock: => T) = times(codeBlock)

  }

  implicit class Fn1[F, T](fn: F => T) {
    @annotation.unspecialized
    def and[A](doAnd: (F, T) => A): F => A =
      f => doAnd(f, fn(f))

    def withEx(exFn: (F, Throwable) => T)(f: F) =
      try {
        fn(f)
      } catch {
        case e:Throwable => exFn(f, e)
      }
  }

  implicit class Case[-F: ClassTag, +T](val fn: F => Option[T]) extends (F => O[T]) {
    val toCase = this

    def unapply(f: F) =
      fn(f)

    def toPfn[A]: PFn[A, T] = {
      case toCase(s) => s
    }

    def apply(f: F) = fn(f)
  }

  implicit class Pfn1[F: ClassTag, T](pfn: Pfn[F, T]) {

    @annotation.unspecialized
    def and[A](doAnd: (F, T) => A) = Case(pfn.lift.and {
      case (f, t) =>
        t.map { x => doAnd(f, x) }
    }).toPfn[F]
  }

  implicit def toPfn[F: ClassTag, T](fn: F => Option[T]): Pfn[F, T] = {
    Case(fn).toPfn[F]
  }

}